package com.lcf.system.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.extension.toolkit.Db;
import com.lcf.framework.constant.SysConstant;
import com.lcf.framework.enums.RedisCachePrefixEnum;
import com.lcf.framework.utils.RedisCacheUtils;
import com.lcf.framework.utils.UserUtils;
import com.lcf.system.dao.SysMenuDao;
import com.lcf.system.dto.menu.SysMenuDTO;
import com.lcf.system.entity.SysMenu;
import com.lcf.system.entity.SysRole;
import com.lcf.system.entity.SysRoleMenu;
import com.lcf.system.entity.SysUserRole;
import com.lcf.system.enums.SysMenuUseEnum;
import com.lcf.system.enums.SysRoleEnum;
import com.lcf.system.manager.CacheManager;
import com.lcf.system.manager.SysMenuManager;
import com.lcf.system.manager.TreeManager;
import com.lcf.system.service.SysMenuService;
import com.lcf.system.vo.menu.SysMenuTreeVO;
import com.lcf.system.vo.menu.SysMenuVO;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 菜单(SysMenu)表服务实现类
 *
 * @author lcf
 * @since 2025-02-25 12:24:22
 */
@Service("sysMenuService")
public class SysMenuServiceImpl extends ServiceImpl<SysMenuDao, SysMenu> implements SysMenuService {
    @Resource
    private RedisCacheUtils redisCacheUtils;
    @Resource
    private SysMenuManager sysMenuManager;
    @Resource
    private TreeManager treeManager;
    @Resource
    private CacheManager cacheManager;

    @Override
    public void add(SysMenuDTO dto) {
        sysMenuManager.checkExist(null, dto);

        BeanUtil.copyProperties(dto, SysMenu.class).insert();

        //删除菜单缓存
        cacheManager.delMenuCache(null);
    }

    @Override
    public void update(long id, SysMenuDTO dto) {
        sysMenuManager.checkId(id);
        sysMenuManager.checkExist(id, dto);

        SysMenu sysMenu = BeanUtil.copyProperties(dto, SysMenu.class);
        sysMenu.setId(id);
        sysMenu.updateById();

        //删除菜单缓存
        cacheManager.delMenuCache(id);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete(long id) {
        //删除菜单缓存
        cacheManager.delMenuCache(id);

        removeById(id);

        //删除角色-菜单
        Db.lambdaUpdate(SysRoleMenu.class).eq(SysRoleMenu::getMenuId, id).remove();
    }

    @Override
    public List<SysMenuTreeVO> getTree() {
        String key = RedisCachePrefixEnum.MENU_TREE.getKey();
        List<SysMenuTreeVO> cache = redisCacheUtils.getList(key);
        if (CollectionUtil.isNotEmpty(cache)) {
            return cache;
        }

        List<SysMenu> list = Db.lambdaQuery(SysMenu.class).list();
        if (CollectionUtils.isEmpty(list)) {
            return Collections.emptyList();
        }

        List<SysMenuTreeVO> tree = treeManager.buildMenuTree(BeanUtil.copyToList(list, SysMenuTreeVO.class), SysConstant.DEFAULT_PARENT_ID);

        redisCacheUtils.cacheList(key, tree);
        redisCacheUtils.expire(key, 1, TimeUnit.DAYS);

        return tree;
    }

    @Override
    public List<SysMenuTreeVO> getTenantTree() {
        String key = RedisCachePrefixEnum.TENANT_MENU_TREE.getKey();
        List<SysMenuTreeVO> cache = redisCacheUtils.getList(key);
        if (CollectionUtil.isNotEmpty(cache)) {
            return cache;
        }

        List<SysMenu> list = Db.lambdaQuery(SysMenu.class)
                .eq(SysMenu::getMenuUse, SysMenuUseEnum.TENANT)
                .orderByAsc(SysMenu::getSortOrder).list();
        if (CollectionUtils.isEmpty(list)) {
            return Collections.emptyList();
        }

        List<SysMenuTreeVO> tree = treeManager.buildMenuTree(BeanUtil.copyToList(list, SysMenuTreeVO.class), SysConstant.DEFAULT_PARENT_ID);

        redisCacheUtils.cacheList(key, tree);
        redisCacheUtils.expire(key, 1, TimeUnit.DAYS);

        return tree;
    }

    @Override
    public List<SysMenuTreeVO> getUserTree() {
        long userId = StpUtil.getLoginIdAsLong();
        long tenantId = UserUtils.getTenantId();
        String key = RedisCachePrefixEnum.USER_MENU.getKey(userId + ":" + tenantId);
        List<SysMenuTreeVO> cache = redisCacheUtils.getList(key);
        if (CollectionUtil.isNotEmpty(cache)) {
            return cache;
        }

        //平台管理员则返回全部平台菜单
        if (UserUtils.isPlatformAdmin()) {
            List<SysMenu> menuList = Db.lambdaQuery(SysMenu.class).eq(SysMenu::getMenuUse, SysMenuUseEnum.PLATFORM.getCode()).list();
            List<SysMenuTreeVO> tree = treeManager.buildMenuTree(BeanUtil.copyToList(menuList, SysMenuTreeVO.class), SysConstant.DEFAULT_PARENT_ID);

            redisCacheUtils.cacheList(key, tree);
            redisCacheUtils.expire(key, 30, TimeUnit.MINUTES);

            return tree;
        }

        List<Long> roleIdList = Db.lambdaQuery(SysUserRole.class)
                .eq(SysUserRole::getUserId, userId)
                .list()
                .stream()
                .map(SysUserRole::getRoleId)
                .distinct()
                .toList();
        if (CollectionUtil.isEmpty(roleIdList)) {
            return Collections.emptyList();
        }

        //租户管理员返回全部租户菜单
        boolean hasAdmin = Db.lambdaQuery(SysRole.class)
                .in(SysRole::getId, roleIdList)
                .eq(SysRole::getRoleCode, SysRoleEnum.ADMIN.getCode())
                .exists();
        if (hasAdmin) {
            List<SysMenu> menuList = Db.lambdaQuery(SysMenu.class).eq(SysMenu::getMenuUse, SysMenuUseEnum.TENANT.getCode()).list();
            List<SysMenuTreeVO> tree = treeManager.buildMenuTree(BeanUtil.copyToList(menuList, SysMenuTreeVO.class), SysConstant.DEFAULT_PARENT_ID);

            redisCacheUtils.cacheList(key, tree);
            redisCacheUtils.expire(key, 30, TimeUnit.MINUTES);

            return tree;
        }

        List<Long> menuIdList = Db.lambdaQuery(SysRoleMenu.class)
                .in(SysRoleMenu::getRoleId, roleIdList)
                .list()
                .stream()
                .map(SysRoleMenu::getMenuId)
                .distinct()
                .toList();

        List<SysMenu> menuList = Db.lambdaQuery(SysMenu.class).in(SysMenu::getId, menuIdList).list();

        List<SysMenuTreeVO> tree = treeManager.buildMenuTree(BeanUtil.copyToList(menuList, SysMenuTreeVO.class), SysConstant.DEFAULT_PARENT_ID);

        redisCacheUtils.cacheList(key, tree);
        redisCacheUtils.expire(key, 30, TimeUnit.MINUTES);

        return tree;
    }

    @Override
    public SysMenuVO getDetail(long id) {
        SysMenu sysMenu = getById(id);

        return BeanUtil.copyProperties(sysMenu, SysMenuVO.class);
    }
}

